/*
 * Generic entry point for the idle threads
 */
#include <seminix/cpumask.h>
#include <seminix/cpu.h>
#include <seminix/smp.h>
#include <seminix/tick.h>
#include <seminix/param.h>
#include <seminix/sched.h>

static int __read_mostly cpu_idle_force_poll;

static int __init cpu_idle_poll_setup(char *__unused)
{
	cpu_idle_force_poll = 1;
	return 0;
}
early_param("nohlt", cpu_idle_poll_setup);

static int __init cpu_idle_nopoll_setup(char *__unused)
{
	cpu_idle_force_poll = 0;
	return 0;
}
early_param("hlt", cpu_idle_nopoll_setup);

void cpu_idle_poll_ctrl(bool enable)
{
	if (enable) {
		cpu_idle_force_poll++;
	} else {
		cpu_idle_force_poll--;
		WARN_ON_ONCE(cpu_idle_force_poll < 0);
	}
}

static inline int cpu_idle_poll(void)
{
	local_irq_enable();
	while (!tif_need_resched())
		cpu_relax();

	return 1;
}

/* Weak implementations for optional arch specific functions */
void __weak arch_cpu_idle_prepare(void) { }
void __weak arch_cpu_idle_enter(void) { }
void __weak arch_cpu_idle_exit(void) { }
void __weak arch_cpu_idle_dead(void) { }
void __weak arch_cpu_idle(void)
{
	cpu_idle_force_poll = 1;
	local_irq_enable();
}

/*
 * Generic idle loop implementation
 */
static void cpu_idle_loop(void)
{
    while (1) {
        tick_nohz_idle_enter();

        while (!need_resched()) {
            rmb();

            if (cpu_is_offline(smp_processor_id()))
                arch_cpu_idle_dead();

            local_irq_disable();
            arch_cpu_idle_enter();

			/*
			 * In poll mode we reenable interrupts and spin.
			 *
			 * Also if we detected in the wakeup from idle
			 * path that the tick broadcast device expired
			 * for us, we don't want to go deep idle as we
			 * know that the IPI is going to arrive right
			 * away
			 */
			if (cpu_idle_force_poll) {
				cpu_idle_poll();
			} else {
				if (!current_clr_polling_and_test()) {
					arch_cpu_idle();
					WARN_ON_ONCE(irqs_disabled());
				} else {
					local_irq_enable();
				}
				__current_set_polling();
			}
            arch_cpu_idle_exit();
        }

		/*
		 * Since we fell out of the loop above, we know
		 * TIF_NEED_RESCHED must be set, propagate it into
		 * PREEMPT_NEED_RESCHED.
		 *
		 * This is required because for polling idle loops we will
		 * not have had an IPI to fold the state for us.
		 */
		preempt_set_need_resched();
        tick_nohz_idle_exit();
        schedule_preempt_disabled();
    }
}

void cpu_startup_entry(void)
{
    __current_set_polling();
    arch_cpu_idle_prepare();
    cpu_idle_loop();
}
